/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

#pragma once

#include "Id/CubismId.hpp"
#include "Math/CubismVector2.hpp"
#include "Model/CubismModel.hpp"

namespace Live2D
{
    namespace Cubism::Framework
    {

        /**
         * @brief 物理演算の適用先の種類
         *
         * 物理演算の適用先の種類。
         */
        enum CubismPhysicsTargetType
        {
            CubismPhysicsTargetType_Parameter, ///< パラメータに対して適用
        };

        /**
         * @brief 物理演算の入力の種類
         *
         * 物理演算の入力の種類。
         */
        enum CubismPhysicsSource
        {
            CubismPhysicsSource_X,     ///< X軸の位置から
            CubismPhysicsSource_Y,     ///< Y軸の位置から
            CubismPhysicsSource_Angle, ///< 角度から
        };

        /**
         * @brief 物理演算で使用する外部の力
         *
         * 物理演算で使用する外部の力。
         */
        struct PhysicsJsonEffectiveForces
        {
            CubismVector2 Gravity; ///< 重力
            CubismVector2 Wind;    ///< 風
        };

        /**
         * @brief 物理演算のパラメータ情報
         *
         * 物理演算のパラメータ情報。
         */
        struct CubismPhysicsParameter
        {
            CubismIdHandle Id;                  ///< パラメータID
            CubismPhysicsTargetType TargetType; ///< 適用先の種類
        };

        /**
         * @brief 物理演算の正規化情報
         *
         * 物理演算の正規化情報。
         */
        struct CubismPhysicsNormalization
        {
            csmFloat32 Minimum; ///< 最大値
            csmFloat32 Maximum; ///< 最小値
            csmFloat32 Default; ///< デフォルト値
        };

        /**
         * @brief 物理演算の演算に使用する物理点の情報
         *
         * 物理演算の演算に使用する物理点の情報。
         */
        struct CubismPhysicsParticle
        {
            CubismVector2 InitialPosition; ///< 初期位置
            csmFloat32 Mobility;           ///< 動きやすさ
            csmFloat32 Delay;              ///< 遅れ
            csmFloat32 Acceleration;       ///< 加速度
            csmFloat32 Radius;             ///< 距離
            CubismVector2 Position;        ///< 現在の位置
            CubismVector2 LastPosition;    ///< 最後の位置
            CubismVector2 LastGravity;     ///< 最後の重力
            CubismVector2 Force;           ///< 現在かかっている力
            CubismVector2 Velocity;        ///< 現在の速度
        };

        /**
         * @brief 物理演算の物理点の管理
         *
         * 物理演算の物理点の管理。
         */
        struct CubismPhysicsSubRig
        {
            int InputCount;                                   ///< 入力の個数
            int OutputCount;                                  ///< 出力の個数
            int ParticleCount;                                ///< 物理点の個数
            int BaseInputIndex;                               ///< 入力の最初のインデックス
            int BaseOutputIndex;                              ///< 出力の最初のインデックス
            int BaseParticleIndex;                            ///< 物理点の最初のインデックス
            CubismPhysicsNormalization NormalizationPosition; ///< 正規化された位置
            CubismPhysicsNormalization NormalizationAngle;    ///< 正規化された角度
        };

        /**
         * @brief 正規化されたパラメータの取得関数の宣言
         *
         * 正規化されたパラメータの取得関数の宣言。
         *
         * @param[out]      targetTranslation       演算結果の移動値
         * @param[out]      targetAngle             演算結果の角度
         * @param[in]       value                   パラメータの値
         * @param[in]       parameterMinimumValue   パラメータの最小値
         * @param[in]       parameterMaximumValue   パラメータの最大値
         * @param[in]       parameterDefaultValue   パラメータのデフォルト値
         * @param[in]       normalizationPosition   正規化された位置
         * @param[in]       normalizationAngle      正規化された角度
         * @param[in]       isInverted              値が反転されているか？
         * @param[in]       weight                  重み
         */
        typedef void (*NormalizedPhysicsParameterValueGetter)(CubismVector2 *targetTranslation, csmFloat32 *targetAngle, csmFloat32 value,
                                                              csmFloat32 parameterMinimumValue, csmFloat32 parameterMaximumValue,
                                                              csmFloat32 parameterDefaultValue,
                                                              CubismPhysicsNormalization *normalizationPosition,
                                                              CubismPhysicsNormalization *normalizationAngle, int isInverted, csmFloat32 weight);

        /**
         * @brief 物理演算の値の取得関数の宣言
         *
         * 物理演算の値の取得関数の宣言。
         *
         * @param[in]       translation     移動値
         * @param[in]       particles       物理点のリスト
         * @param[in]       isInverted      値が反転されているか？
         * @param[in]       parentGravity   重力
         * @return  値
         */
        typedef csmFloat32 (*PhysicsValueGetter)(CubismVector2 translation, CubismPhysicsParticle *particles, int particleIndex, int isInverted,
                                                 CubismVector2 parentGravity);

        /**
         * @brief 物理演算のスケールの取得関数の宣言
         *
         * 物理演算のスケールの取得関数の宣言。
         *
         * @param[in]   translationScale    移動値のスケール
         * @param[in]   angleScale          角度のスケール
         * @return  スケール値
         */
        typedef csmFloat32 (*PhysicsScaleGetter)(CubismVector2 translationScale, csmFloat32 angleScale);

        /**
         * @brief 物理演算の入力情報
         *
         * 物理演算の入力情報。
         */
        struct CubismPhysicsInput
        {
            CubismPhysicsParameter Source;                                     ///< 入力元のパラメータ
            int SourceParameterIndex;                                          ///< 入力元のパラメータのインデックス
            csmFloat32 Weight;                                                 ///< 重み
            csmInt16 Type;                                                     ///< 入力の種類
            csmInt16 Reflect;                                                  ///< 値が反転されているかどうか
            NormalizedPhysicsParameterValueGetter GetNormalizedParameterValue; ///< 正規化されたパラメータ値の取得関数
        };

        /**
         * @brief 物理演算の出力情報
         *
         * 物理演算の出力情報。
         */
        struct CubismPhysicsOutput
        {
            CubismPhysicsParameter Destination; ///< 出力先のパラメータ
            int DestinationParameterIndex;      ///< 出力先のパラメータのインデックス
            int VertexIndex;                    ///< 振り子のインデックス
            CubismVector2 TranslationScale;     ///< 移動値のスケール
            csmFloat32 AngleScale;              ///< 角度のスケール
            csmFloat32 Weight;                  /// 重み
            CubismPhysicsSource Type;           ///< 出力の種類
            csmInt16 Reflect;                   ///< 値が反転されているかどうか
            csmFloat32 ValueBelowMinimum;       ///< 最小値を下回った時の値
            csmFloat32 ValueExceededMaximum;    ///< 最大値をこえた時の値
            PhysicsValueGetter GetValue;        ///< 物理演算の値の取得関数
            PhysicsScaleGetter GetScale;        ///< 物理演算のスケール値の取得関数
        };

        /**
         * @brief 物理演算のデータ
         *
         * 物理演算のデータ。
         */
        struct CubismPhysicsRig
        {
            int SubRigCount;                          ///< 物理演算の物理点の個数
            QVector<CubismPhysicsSubRig> Settings;    ///< 物理演算の物理点の管理のリスト
            QVector<CubismPhysicsInput> Inputs;       ///< 物理演算の入力のリスト
            QVector<CubismPhysicsOutput> Outputs;     ///< 物理演算の出力のリスト
            QVector<CubismPhysicsParticle> Particles; ///< 物理演算の物理点のリスト
            CubismVector2 Gravity;                    ///< 重力
            CubismVector2 Wind;                       ///< 風
        };

    } // namespace Cubism::Framework
} // namespace Live2D
